/******************************************************************************
 *                                                                            *
 * Module:       Altera_UP_VGA_DAC                                            *
 * Description:                                                               *
 *      This module drives the vga dac on Altera's DE2 Board.                 *
 *                                                                            *
 ******************************************************************************/

module Altera_UP_VGA_DAC (
	// inputs
	clk,
	clk_inverted,
	reset,

	red_data_to_vga_display,
	green_data_to_vga_display,
	blue_data_to_vga_display,
	data_valid,

	// bidirectional

	// outputs
	line_counter,
	pixel_counter,
	active_display_interval,
	consecutive_memory_addresses,
	end_of_frame,

	// dac pins
	vga_clk,					//	VGA Clock
	vga_blank,					//	VGA BLANK
	vga_c_sync,					//	VGA COMPOSITE SYNC
	vga_h_sync,					//	VGA H_SYNC
	vga_v_sync,					//	VGA V_SYNC
	vga_red,					//	VGA Red[9:0]
	vga_green,	 				//	VGA Green[9:0]
	vga_blue	   				//	VGA Blue[9:0]
);

/*****************************************************************************
 *                           Parameter Declarations                          *
 *****************************************************************************/

// states
localparam	VGA_STATE_0_RESET			= 1'b0,
			VGA_STATE_1_ACTIVE			= 1'b1;

parameter DELAY_OF_VGA_DATA				= 2;
parameter VGA_MODE						= 0;

parameter H_ACTIVE 						= 640;	// pixels
parameter H_FRONT_PORCH					= 16;	// pixels
parameter H_SYNC						= 96;	// pixels
parameter H_BACK_PORCH 					= 48;	// pixels
parameter H_TOTAL 						= 800;	// pixels

parameter V_ACTIVE 						= 480;	// lines
parameter V_FRONT_PORCH					= 10;	// lines
parameter V_SYNC						= 2;	// lines
parameter V_BACK_PORCH 					= 33;	// lines
parameter V_TOTAL						= 525;	// lines

parameter NUMBER_OF_BITS_FOR_LINES		= 10;
parameter LINE_COUNTER_INCREMENT		= 10'h001;

parameter NUMBER_OF_BITS_FOR_PIXELS		= 10;
parameter PIXEL_COUNTER_INCREMENT		= 10'h001;

parameter NUMBER_OF_BITS_FOR_ADDRESS	= 19;
parameter ADDRESS_INCREMENT				= 19'h00001;


// Parameters for VGA 640x480 at 60 Hz
//`ifdef VGA_MODE_640x480
//`elsif VGA_MODE_800x600
//`else
//`endif

/*****************************************************************************
 *                             Port Declarations                             *
 *****************************************************************************/
// Inputs
input				clk;
input				clk_inverted;
input				reset;

input		[9:0]	red_data_to_vga_display;
input		[9:0]	green_data_to_vga_display;
input		[9:0]	blue_data_to_vga_display;
input				data_valid;

// Bidirectionals

// Outputs
output 	reg	[NUMBER_OF_BITS_FOR_LINES:1]	line_counter;
output 	reg	[NUMBER_OF_BITS_FOR_PIXELS:1]	pixel_counter;
output 	reg			active_display_interval;
output 	reg	[NUMBER_OF_BITS_FOR_ADDRESS:1]	consecutive_memory_addresses;
output	reg			end_of_frame;

// dac pins
output				vga_clk;					//	VGA Clock
output				vga_blank;					//	VGA BLANK
output				vga_c_sync;					//	VGA COMPOSITE SYNC
output				vga_h_sync;					//	VGA H_SYNC
output				vga_v_sync;					//	VGA V_SYNC
output	reg	[9:0]	vga_red;					//	VGA Red[9:0]
output	reg	[9:0]	vga_green;	 				//	VGA Green[9:0]
output	reg	[9:0]	vga_blue;   				//	VGA Blue[9:0]

/*****************************************************************************
 *                 Internal wires and registers Declarations                 *
 *****************************************************************************/
// Internal Wires

// Internal Registers
reg			[DELAY_OF_VGA_DATA:0]	hsync_shift_reg;
reg			[DELAY_OF_VGA_DATA:0]	vsync_shift_reg;
reg			[DELAY_OF_VGA_DATA:0]	csync_shift_reg;
reg			[DELAY_OF_VGA_DATA:0]	blanking_shift_reg;

reg					reset_timing;

reg					hsync_pulse;
reg					vsync_pulse;
reg					csync_pulse;
reg					blanking_pulse;

reg					line_is_active;
reg					pixel_is_active;

reg					early_hsync_pulse;
reg					early_vsync_pulse;

reg					hblanking_pulse;
reg					vblanking_pulse;

// State Machine Registers
reg					ns_vga_controller;
reg					s_vga_controller;


/*****************************************************************************
 *                         Finite State Machine(s)                           *
 *****************************************************************************/

always @(posedge clk)	// sync reset
begin
	if (reset == 1'b1)
	begin
		s_vga_controller <= VGA_STATE_0_RESET;
	end
	else
	begin
		s_vga_controller <= ns_vga_controller;
	end
end

always @(*)
begin
	// Defaults
	ns_vga_controller = VGA_STATE_0_RESET;

    case (s_vga_controller)
	VGA_STATE_0_RESET:
	begin
		if (data_valid == 1'b1)
		begin
			ns_vga_controller = VGA_STATE_1_ACTIVE;
		end
		else
		begin
			ns_vga_controller = VGA_STATE_0_RESET;
		end
	end
	VGA_STATE_1_ACTIVE:
	begin
		ns_vga_controller = VGA_STATE_1_ACTIVE;
	end
	default:
	begin
		ns_vga_controller = VGA_STATE_0_RESET;
	end
	endcase
end

/*****************************************************************************
 *                             Sequential logic                              *
 *****************************************************************************/

always @ (posedge clk)
begin
	if ((reset == 1'b1) || (s_vga_controller == VGA_STATE_0_RESET))
	begin
		blanking_shift_reg 	<= {(DELAY_OF_VGA_DATA + 1){1'b0}};
		csync_shift_reg 	<= {(DELAY_OF_VGA_DATA + 1){1'b1}};
		hsync_shift_reg 	<= {(DELAY_OF_VGA_DATA + 1){1'b1}};
		vsync_shift_reg 	<= {(DELAY_OF_VGA_DATA + 1){1'b1}};

		vga_red				<= 10'h000;
		vga_green			<= 10'h000;
		vga_blue			<= 10'h000;
		
		reset_timing		<= 1'b1;
	end
	else
	begin
		blanking_shift_reg 	<= 
			{blanking_shift_reg[(DELAY_OF_VGA_DATA - 1):0],~blanking_pulse};
		csync_shift_reg	 	<=
			{csync_shift_reg[(DELAY_OF_VGA_DATA - 1):0],~csync_pulse};
		hsync_shift_reg	 	<=
			{hsync_shift_reg[(DELAY_OF_VGA_DATA - 1):0],~hsync_pulse};
		vsync_shift_reg		<=
			{vsync_shift_reg[(DELAY_OF_VGA_DATA - 1):0],~vsync_pulse};

		vga_red				<= red_data_to_vga_display;
		vga_green			<= green_data_to_vga_display;
		vga_blue			<= blue_data_to_vga_display;

		reset_timing		<= 1'b0;
	end
end

always @ (posedge clk)
begin
	if (reset_timing == 1'b1)
	begin
		pixel_counter <= H_TOTAL - 3; // {NUMBER_OF_BITS_FOR_PIXELS{1'b0}};
	end
	else if (pixel_counter == (H_TOTAL - 1))
	begin
		// last pixel in the line
		pixel_counter <= {NUMBER_OF_BITS_FOR_PIXELS{1'b0}};
	end
	else 
	begin
		pixel_counter <= pixel_counter + PIXEL_COUNTER_INCREMENT;
	end
end

always @ (posedge clk)
begin
	if (reset_timing == 1'b1)
	begin
		line_counter <= V_TOTAL - 1; // {NUMBER_OF_BITS_FOR_LINES{1'b0}};
	end
	else if ((line_counter == (V_TOTAL - 1)) &&
			(pixel_counter == (H_TOTAL - 1)))
	begin
		// last pixel in last line of frame
		line_counter <= {NUMBER_OF_BITS_FOR_LINES{1'b0}};
	end
	else if (pixel_counter == (H_TOTAL - 1))
	begin
		// last pixel but not last line
		line_counter <= line_counter + LINE_COUNTER_INCREMENT;
	end
end

/////////////////////////////////////////////////////////////////////////////////////////////

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		pixel_is_active <= 1'b0;
	end
	else if (pixel_counter == (H_ACTIVE - 2))
	begin
		// last active pixel
		pixel_is_active <= 1'b0;
	end
	else if (pixel_counter == (H_TOTAL - 2))
	begin
		// last pixel
		pixel_is_active <= 1'b1;
	end
end

always @ (posedge clk)
begin
	if (reset_timing == 1'b1)
	begin
		line_is_active <= 1'b0;
	end
	else if ((line_counter == (V_ACTIVE - 1)) && 
			(pixel_counter == (H_ACTIVE - 2)))
//			(pixel_counter == (H_TOTAL - 2)))
	begin
		// last active line
		line_is_active <= 1'b0;
	end
	else if ((line_counter == (V_TOTAL - 1)) &&
			(pixel_counter == (H_TOTAL - 2)))
	begin
		// last line
		line_is_active <= 1'b1;
	end
end

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		active_display_interval	<= 1'b0;
	end
	else if ((line_is_active == 1'b1) &&
			(pixel_is_active == 1'b1)) 
	begin
		active_display_interval	<= 1'b1;
	end
	else
	begin
		active_display_interval	<= 1'b0;
	end
end

/////////////////////////////////////////////////////////////////////////////////////////////

always @ (posedge clk)
begin
	if (reset_timing == 1'b1)
	begin
		consecutive_memory_addresses <= {NUMBER_OF_BITS_FOR_ADDRESS{1'b0}};
	end
	else if (active_display_interval == 1'b1)
	begin
		consecutive_memory_addresses <= 
			consecutive_memory_addresses + ADDRESS_INCREMENT;
	end
	else if ((line_counter == (V_TOTAL - 1)) &&
			(pixel_counter == (H_TOTAL - 2)))
	begin
		consecutive_memory_addresses <= {NUMBER_OF_BITS_FOR_ADDRESS{1'b0}};
	end
end


always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		end_of_frame <= 1'b0;
	end
	else
	begin
		if ((line_counter == (V_TOTAL - 1)) &&
			(pixel_counter == (H_TOTAL - 2)))
		begin
			end_of_frame <= 1'b1;
		end
		else
		begin
			end_of_frame <= 1'b0;
		end
	end
end



/////////////////////////////////////////////////////////////////////////////////////////////

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		early_hsync_pulse <= 1'b0;
	end
	else if (pixel_counter == (H_ACTIVE + H_FRONT_PORCH - 2))
//	else if (pixel_counter == (H_ACTIVE + 2/* + H_FRONT_PORCH - 2*/))
	begin
		// start of horizontal sync
		early_hsync_pulse <= 1'b1;	
	end
	else if (pixel_counter == (H_TOTAL - H_BACK_PORCH - 2))
//	else if (pixel_counter == (H_ACTIVE + H_SYNC + 2/* - 2*/))
	begin
		// end of horizontal sync
		early_hsync_pulse <= 1'b0;	
	end
end

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		early_vsync_pulse <= 1'b0;
	end
	else if ((line_counter == (V_ACTIVE + V_FRONT_PORCH - 1)) && 
			(pixel_counter == (H_TOTAL - 2)))
	begin
		// start of vertical sync
		early_vsync_pulse <= 1'b1;
	end
	else if ((line_counter == (V_TOTAL - V_BACK_PORCH - 1)) && 
			(pixel_counter == (H_TOTAL - 2)))
	begin
		// end of vertical sync
		early_vsync_pulse <= 1'b0;
	end
end

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		hsync_pulse <= 1'b0;
		vsync_pulse <= 1'b0;
	end
	else
	begin
		hsync_pulse <= early_hsync_pulse;
		vsync_pulse <= early_vsync_pulse;
	end
end

always @ (posedge clk)
begin
	if (reset_timing == 1'b1)
	begin
		csync_pulse	<= 1'b0;
	end
	else if ((early_hsync_pulse == 1'b1) &&
			 (early_vsync_pulse == 1'b1)) 
	begin
		csync_pulse	<= 1'b0;
	end
	else if ((early_hsync_pulse == 1'b1) ||
			 (early_vsync_pulse == 1'b1)) 
	begin
		csync_pulse	<= 1'b1;
	end
	else
	begin
		csync_pulse	<= 1'b0;
	end
end


/////////////////////////////////////////////////////////////////////////////////////////////

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		hblanking_pulse	<= 1'b0;
	end
	else if (pixel_counter == (H_ACTIVE - 2))
//	else if (pixel_counter == (H_ACTIVE + H_FRONT_PORCH - 2))
	begin
		hblanking_pulse	<= 1'b1;
	end
	else if (pixel_counter == (H_TOTAL - 2))
//	else if (pixel_counter == (H_FRONT_PORCH - 2))
	begin
		hblanking_pulse	<= 1'b0;
	end
end

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		vblanking_pulse	<= 1'b0;
	end
	else if ((line_counter == (V_ACTIVE - 1)) &&
			(pixel_counter == (H_TOTAL - 2))) 
	begin
		vblanking_pulse	<= 1'b1;
	end
	else if ((line_counter == (V_TOTAL - 1)) &&
			(pixel_counter == (H_TOTAL - 2))) 
	begin
		vblanking_pulse	<= 1'b0;
	end
end

always @ (posedge clk) 
begin
	if (reset_timing == 1'b1)
	begin
		blanking_pulse	<= 1'b0;
	end
	else if ((hblanking_pulse == 1'b1) ||
			 (vblanking_pulse == 1'b1)) 
	begin
		blanking_pulse	<= 1'b1;
	end
	else
	begin
		blanking_pulse	<= 1'b0;
	end
end

/*****************************************************************************
 *                            Combinational logic                            *
 *****************************************************************************/

assign vga_clk 		= clk_inverted;

assign vga_c_sync	= csync_shift_reg[DELAY_OF_VGA_DATA];
assign vga_blank	= blanking_shift_reg[DELAY_OF_VGA_DATA];
assign vga_h_sync	= hsync_shift_reg[DELAY_OF_VGA_DATA];
assign vga_v_sync	= vsync_shift_reg[DELAY_OF_VGA_DATA];

/*****************************************************************************
 *                              Internal Modules                             *
 *****************************************************************************/


endmodule

